package persistence.repository;

import java.util.Collection;

import persistence.PersistenceController;
import persistence.Persistent;
import util.Bug;
import util.NotFoundException;

/**
 * responsible of maintaining the Persistent interface
 * invariant and, of course, abstracting member persistence.
 *
 * @param <Element> type of elements in this repository.

 * @author idan
 */
public class Repository<Element extends Persistent>
{
	private PersistenceController persistence ;

	/**
	 * @param persistence persistence controller for the elements in this repository.
	 * @pre 'persistence' loads/stores elements of type 'Element'.
	 * @post repository uses 'persistence' to persist its elements.
	 */
	public Repository(final PersistenceController persistence)
	{
		super() ;
		this.persistence = persistence ;
	}

	/**
	 * close down this repository.
	 * @post the repository no longer accept requests. further requests might throw exceptions,
	 * but in any case will not be stored to persistence.
	 */
	public void close()
	{
		this.persistence.close() ;
	}

	/**
	 * @param id id to check for.
	 * @return whether an object with the id 'id' exists in this repository.
	 */
	public boolean contains(final long id)
	{
		Persistent p = this.persistence.get(id) ;
		return p != null ;
	}

	/**
	 * @param id id of an object to destroy.
	 * @throws NotFoundException if the object does not exist in the repository.
	 * @pre this.contains(id)
	 * @post !this.contains(id)
	 */
	public void destroy(final long id) throws NotFoundException
	{
		this.persistence.startTransaction() ;
		this.persistence.delete(id) ;
		this.persistence.commitTransaction() ;
	}

	/**
	 * flush the repository's contents to persistence.
	 * @post all elements in the repository are saved to persistence.
	 */
	public void flush()
	{
		this.persistence.startTransaction() ;
		this.persistence.commitTransaction() ;
	}

	/**
	 * @param id id of the requested element.
	 * @return the element in this repository whose id is 'id'.
	 * @throws NotFoundException if the repository does not contain an element with such id.
	 * @pre this.contains(id)
	 */
	@SuppressWarnings("unchecked")
	public Element get(final long id) throws NotFoundException
	{
		try
		{
			this.persistence.startTransaction() ;
			Persistent p = this.persistence.get(id) ;
			this.persistence.commitTransaction() ;
			Element result = (Element)p ;
			if (result != null)
			{
				return result ;
			}
			else
			{
				throw new NotFoundException(id) ;
			}
		}
		catch (ClassCastException e)
		{
			throw new Bug("repository works with the wrong type") ;
		}
	}

	/**
	 * @return all elements in this repository.
	 * this is probably an expensive operation.
	 */
	@SuppressWarnings("unchecked")
	public Collection<Element> getAll()
	{
		this.persistence.startTransaction() ;
		Collection<Element> elements = (Collection<Element>)this.persistence.getAll() ;
		this.persistence.commitTransaction() ;
		return elements ;
	}

	/**
	 * @param element element to insert into the repository.
	 * @post 'element' is assigned a new id.
	 */
	protected void put(final Element element)
	{
		this.persistence.startTransaction() ;
		this.persistence.put(element) ;
		this.persistence.commitTransaction() ;
	}
}
